package io.renren.modules.minio.util;

import cn.hutool.core.io.IoUtil;
import io.minio.*;
import io.minio.http.Method;
import io.minio.messages.DeleteError;
import io.minio.messages.DeleteObject;
import io.renren.common.exception.RenException;
import io.renren.modules.minio.properties.MinioProperties;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.tomcat.util.http.fileupload.IOUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.LinkedList;
import java.util.List;

/**
 * minio操作文件工具类
 *
 * @author txb
 */
@Slf4j
@Component
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
public class MinioFileUtil {

    private final MinioProperties properties;

    private volatile static MinioClient minioClient;

    private MinioClient getMinioClient() {
        if (minioClient == null) {
            synchronized (MinioFileUtil.class) {
                if (minioClient == null) {
                    try {
                        minioClient = MinioClient
                                .builder()
                                .endpoint(properties.getEndPoint())
                                .credentials(properties.getAccessKey(), properties.getSecretKey())
                                .build();
                    } catch (Exception e) {
                        log.error("{}，minio服务器连接出错，{}", properties.getEndPoint(), e.getMessage());
                        throw new RenException("minio服务器连接出错");
                    }
                }
            }
        }
        if (minioClient == null) {
            throw new RenException("获取minio连接失败");
        }
        try {
            boolean found =
                    minioClient.bucketExists(BucketExistsArgs.builder().bucket(properties.getBucketName()).build());
            if (!found) {
                throw new RenException("minio的bucket不存在，请先创建");
            }
        } catch (Exception e) {
            log.error("{}，minio服务器判断bucket是否存在出错，{}", properties.getEndPoint(), e.getMessage());
            throw new RenException("minio服务器判断bucket是否存在出错");
        }
        return minioClient;
    }

    /**
     * 删除指定的文件
     *
     * @param filePath 要被删除的文件路径，不能为空
     */
    public Boolean deleteFile(List<String> filePath) throws Exception {
        if (CollectionUtils.isEmpty(filePath)) {
            throw new RenException("要被删除的文件路径不能为空");
        }

        // 获取连接
        MinioClient minioClient = getMinioClient();
        List<DeleteObject> objects = new LinkedList<>();
        for (String path : filePath) {
            objects.add(new DeleteObject(path));
        }
        Iterable<Result<DeleteError>> results = minioClient.removeObjects(
                RemoveObjectsArgs
                        .builder()
                        .bucket(properties.getBucketName())
                        .objects(objects)
                        .build()
        );

        boolean res = true;
        for (Result<DeleteError> result : results) {
            DeleteError error = result.get();
            log.error("Error in deleting object {}; {}", error.objectName(), error.message());
            res = false;
        }
        return res;
    }

    /**
     * 上传文件
     *
     * @param filePath    要被上传文件的路径，不能为空
     * @param ins         要被上传文件的输入流，不能为空
     * @param contentType 文件类型
     */
    public void uploadFile(String filePath, InputStream ins, String contentType) throws Exception {
        if (!StringUtils.hasText(filePath)) {
            throw new RenException("要被上传的文件路径不能为空");
        }

        if (ins == null) {
            throw new RenException("要被上传的文件流不能为空");
        }

        // 获取连接
        MinioClient minioClient = getMinioClient();

        minioClient.putObject(
                PutObjectArgs
                        .builder()
                        .bucket(properties.getBucketName())
                        .object(filePath)
                        .stream(ins, ins.available(), -1L)
                        .contentType(contentType)
                        .build()
        );
    }

    /**
     * 获取指定文件的输入流
     *
     * @param filePath 文件路径，不能为
     * @return InputStream 文件流
     */
    public InputStream getInputStream(String filePath) throws Exception {
        if (!StringUtils.hasText(filePath)) {
            throw new RenException("文件路径不能为空");
        }

        // 获取连接
        MinioClient minioClient = getMinioClient();
        return minioClient.getObject(
                GetObjectArgs
                        .builder()
                        .object(filePath)
                        .bucket(properties.getBucketName())
                        .build()
        );
    }

    public String getUrl(String filePath) {
        MinioClient minioClient = getMinioClient();
        String path = null;
        try {
            path = minioClient.getPresignedObjectUrl(
                    GetPresignedObjectUrlArgs
                            .builder()
                            .method(Method.GET)
                            .bucket(properties.getBucketName())
                            .object(filePath)
                            .build()
            );
            if (StringUtils.hasText(path)) {
                path = path.replace(properties.getEndPoint(), properties.getPrefix());
            }
        } catch (Exception e) {
            log.error("minio获取路径失败", e);
        }
        return path;
    }

    /**
     * 写出文件到指定的输出流
     *
     * @param filePath     文件路径，不能为空
     * @param outputStream 要被写出的输出流
     */
    public void writeOutputStream(String filePath, OutputStream outputStream) throws Exception {
        if (!StringUtils.hasText(filePath)) {
            throw new RenException("文件下载路径不能为空");
        }
        if (outputStream == null) {
            throw new RenException("输出流不能为空");
        }
        // 获取连接
        MinioClient minioClient = getMinioClient();
        try (InputStream ins = minioClient.getObject(
                GetObjectArgs
                        .builder()
                        .bucket(properties.getBucketName())
                        .object(filePath)
                        .build()
        )) {
            IOUtils.copy(ins, outputStream);
        }
    }

    /**
     * 图片预览
     *
     * @param filePath     文件路径，不能为空
     * @param outputStream 要被写出的输出流
     */
    public void view(String filePath, OutputStream outputStream) throws Exception {
        if (!StringUtils.hasText(filePath)) {
            throw new RenException("文件路径不能为空");
        }
        if (outputStream == null) {
            throw new RenException("输出流不能为空");
        }
        // 获取连接
        MinioClient minioClient = getMinioClient();
        try (InputStream ins = minioClient.getObject(
                GetObjectArgs
                        .builder()
                        .bucket(properties.getBucketName())
                        .object(filePath)
                        .build()
        ); ByteArrayOutputStream outStream = new ByteArrayOutputStream()) {
            byte[] buffer = new byte[1024];
            int len;
            while ((len = ins.read(buffer)) != -1) {
                outStream.write(buffer, 0, len);
            }
            outputStream.write(outStream.toByteArray());
            outputStream.flush();
            outputStream.close();
        }
    }

    /**
     * 读取文件内容
     *
     * @param filePath 文件路径，不能为空
     */
    public String getFileContent(String filePath) throws Exception {
        if (!StringUtils.hasText(filePath)) {
            throw new RenException("文件路径不能为空");
        }
        // 获取连接
        MinioClient minioClient = getMinioClient();
        try (InputStream ins = minioClient.getObject(
                GetObjectArgs
                        .builder()
                        .bucket(properties.getBucketName())
                        .object(filePath)
                        .build()
        )) {
            return IoUtil.readUtf8(ins);
        }
    }

    /**
     * 读取文件内容
     *
     * @param filePath 文件路径，不能为空
     */
    public byte[] getFileContentByte(String filePath) throws Exception {
        if (!StringUtils.hasText(filePath)) {
            throw new RenException("文件路径不能为空");
        }
        // 获取连接
        MinioClient minioClient = getMinioClient();
        try (InputStream ins = minioClient.getObject(
                GetObjectArgs
                        .builder()
                        .bucket(properties.getBucketName())
                        .object(filePath)
                        .build()
        )) {
            return IoUtil.readBytes(ins);
        }
    }
}
